package com.zhss.eshop.common.domain;

import com.zhss.eshop.common.util.DomainType;
import com.zhss.eshop.common.util.BeanCopierUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

/**
 * 公共基础基类
 *
 * @Date: 2021-03-01
 **/
public class AbstractObject {
    private Logger logger = LoggerFactory.getLogger(AbstractObject.class);

    /**
     * 浅度克隆
     *
     * @Param t 目标对象
     * @Return T 克隆后的对象
     * @date 2021/3/5
     */
    public <T> T clone(T t) throws Exception {
        BeanCopierUtils.copyProperties(this, t);
        return t;
    }

    /**
     * 浅度克隆
     *
     * @Param clazz 目标class对象
     * @Return T 克隆后的对象
     * @date 2021/3/5
     */
    public <T> T clone(Class<T> clazz) throws Exception {
        T target = clazz.newInstance();
        BeanCopierUtils.copyProperties(this, target);
        return target;
    }

    /**
     * 深度克隆
     *
     * @Param clazz 目标class对象
     * @Param clazz 目标class对象
     * @Return T 克隆后的对象
     * @Date: 2021/2/5
     */
    public <T> T clone(Class<T> clazz, Integer cloneDirection) {
        T target = null;
        try {
            // 完成基本字段的浅克隆
            target = clazz.newInstance();
            BeanCopierUtils.copyProperties(this, target);

            // CategoryDTO.class, 完成所有List类型的深度克隆
            Class<?> thisClazz = this.getClass();

            // 获取CategoryDTO里面所有的属性
            // getDeclaredFields, 获得某个类的所有声明的字段，即包括public、private和proteced，但是不包括父类的申明字段
            Field[] fields = thisClazz.getDeclaredFields();

            // 遍历每个field
            for (Field field : fields) {
                // 利用 setAccessible 访问私有对象的值
                field.setAccessible(true);

                // 如果field是一个List的类型，field的类型为List,不是获取具体的List
                if (field.getType() == List.class) {

                    // 获取List集合中的泛型类型：要获取具体的List<T>中的T, 可以通过field.getGenericType来进行获取
                    Class<?> genericClazz = getListGenericType(field);

                    assert genericClazz != null;

                    // 获取要克隆的目标对象：假设 cloneDirection 是反向，此时获取到的是RelationVO
                    Class<?> targetClass = getTargetClassName(genericClazz, cloneDirection);

                    // 获取到实际的List集合
                    List clonedList = new ArrayList<>();

                    // 获取实际的List集合
                    List<?> sourceList = (List<?>) field.get(this); // 获取到的是List<RelationDTO>集合

                    // 将克隆List到目标List里面去
                    cloneList(sourceList, clonedList, targetClass, cloneDirection);

                    // 获取目标对象设置list的方法名称, setRelations，属性名称为：relations,获取到一个setRelations
                    Method targetFieldSetMethod = getSetCloneListFieldMethod(clazz, field);

                    // targetFieldSetMethod非空判断
                    assert targetFieldSetMethod != null;

                    // 通过目标方法设置属性, target是CategoryVO对象，此时就是调用CategoryVO的setRelations方法，将克隆好的List<RelationVO>给设置进去
                    targetFieldSetMethod.invoke(target, clonedList);
                }
            }


        } catch (Exception e) {
            logger.error("AbstractObject.clone error,clazz : {}", clazz, e);
        }
        return target;
    }

    /**
     * 将一个List 克隆到  另外一个List
     *
     * @param sourceList
     * @param targetList
     * @param targetClass
     * @param cloneDirection
     * @return
     * @date 2021/3/5
     */
    private void cloneList(List sourceList, List targetList,
                           Class<?> targetClass, Integer cloneDirection) throws Exception {
        if (sourceList == null || targetList == null) {
            return;
        }

        // 遍历源对象中的List对象
        for (Object o : sourceList) {
            // 将每个List集合
            AbstractObject targetObject = (AbstractObject) o;

            // 将集合中的RelationDTO，调用其克隆方法，将其往RelationVO去克隆
            AbstractObject cloneObject = (AbstractObject) targetObject.clone(targetClass, cloneDirection);

            // 获取到RelationVO集合
            targetList.add(cloneObject);
        }
    }

    /**
     * 获取List集合的泛型类型
     *
     * @param field
     * @return {@link Class<?>}
     * @date 2021/3/5
     */
    private Class<?> getListGenericType(Field field) {
        // genericType = List<RelationDTO>, 不是List
        Type genericType = field.getGenericType();

        if (genericType instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            return (Class<?>) parameterizedType.getActualTypeArguments()[0];
        }
        return null;
    }

    /**
     * 获取设置克隆好的list方法名称
     *
     * @param field 属性
     * @return {@link Method} 目标对象的方法对象
     * @date 2021/3/5
     */
    private Method getSetCloneListFieldMethod(Class<?> clazz, Field field) throws Exception {
        String name = field.getName();
        String setMethodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
        Method[] declaredMethods = clazz.getDeclaredMethods();

        Method targetFieldSetMethod = null;
        for (Method declaredMethod : declaredMethods) {
            if (declaredMethod.getName().equals(setMethodName)) {
                targetFieldSetMethod = declaredMethod;
                break;
            }
        }
        return targetFieldSetMethod;
    }

    /**
     * 获取目标的类名
     *
     * @param genericClazz List<T> 中的T类型
     * @param cloneDirection 转换方向
     * @return {@link String}
     * @date 2021/3/5
     */
    private Class<?> getTargetClassName(Class<?> genericClazz, Integer cloneDirection) throws Exception {
        String className = genericClazz.getName();
        String cloneClassName = null;

        // 如果是正向转换：VO->DTO->DO
        if (cloneDirection.equals(CloneDirection.FORWARD)) {
            if (className.endsWith(DomainType.VO)) {
                cloneClassName = className.substring(0, className.length() - 2) + "DTO";
            } else if (className.endsWith(DomainType.DTO)) {
                cloneClassName = className.substring(0, className.length() - 3) + "DO";
            }
        }

        // 如果是逆向转换：DO->DTO->VO
        if (cloneDirection.equals(CloneDirection.OPPOSITE)) {
            if (className.endsWith(DomainType.DO)) {
                // 原来的是DO，转换成DTO
                cloneClassName = className.substring(0, className.length() - 2) + "DTO";
            } else if (className.endsWith(DomainType.DTO)) {
                // 原来的是DTO，转换成VO
                cloneClassName = className.substring(0, className.length() - 3) + "VO";
            }
        }

        return Class.forName(cloneClassName);
    }
}
